﻿using NHibernate;
using NHibernate.Criterion;
using NovelDB;
using NovelDB.Entity;
using NovelDB.Helper;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System;

namespace NovelLibEx.Manager
{
    public class DBNovelManager
    {
        private ISession Session { get; set; }

        public DBNovelManager()
        {
            NHibernateHelper helper = new NHibernateHelper();
            Session = helper.GetSession();
        }

        /// <summary>
        /// 根据从小说目录中解析出来的内容，到数据库中查找
        /// 首先，看是不是存在同一个网站下面的同名小说，如果存在继续，不存在，则创建一个
        /// 接着查找小说下面对应的章节是否存在，存在继续，不存在，创佳一个
        /// 最后查找章节下面对应的链接是否存在，存在继续，不存在，创建一个
        /// </summary>
        /// <param name="novelOfWeb">从网站解析出来的小说目录</param>
        /// <param name="isForceUpdate">是否强制更新。如果设置为true，
        /// 则不管以前是不是有内容，都全部更新一遍。
        /// 具体的做法是删除以前下载的小说</param>
        public Novel CheckNovel(Novel novelOfWeb, bool isForceUpdate = false)
        {
            Novel nvOfDB = CriticalQuery.GetNovelByNameAndSite(Session, novelOfWeb.Name, novelOfWeb.SiteName);
            if (isForceUpdate && nvOfDB != null)
            {
                Session.Delete(nvOfDB);
                Session.Flush();
                nvOfDB = null;
            }

            if (nvOfDB == null)
            {
                nvOfDB = new Novel
                {
                    Name = novelOfWeb.Name,
                    SiteName = novelOfWeb.SiteName,
                    NovelUrl = novelOfWeb.NovelUrl,
                    Description = novelOfWeb.Description,
                    NovelChapters = new List<NovelChapter>()
                };

                Session.SaveOrUpdate(nvOfDB);
                Session.Flush();
            }

            foreach (var chap in novelOfWeb.NovelChapters)
            {
                NovelChapter chapterDB = CriticalQuery.GetChapterByNovelIdAndChapterId(Session, nvOfDB, chap.ChapterId);
                if (chapterDB == null)
                {
                    chapterDB = new NovelChapter
                    {
                        Name = chap.Name,
                        Novel = nvOfDB,
                        ChapterId = chap.ChapterId,
                        NovelLinks = new List<NovelLink>()
                    };

                    Session.SaveOrUpdate(chapterDB);
                    Session.Flush();
                }

                for(int i = chap.NovelLinks.Count - 1; i >= 0; i--)
                {
                    NovelLink link = chap.NovelLinks[i];

                    NovelLink linkDB = CriticalQuery.GetLinkByLinkId(Session, nvOfDB.Id, link.LinkId);
                    if (linkDB == null)
                    {
                        linkDB = new NovelLink
                        {
                            Href = link.Href,
                            Title = link.Title,
                            NovelChapter = chapterDB,
                            Description = link.Description,
                            LinkId = link.LinkId,
                            SiteUrl = link.SiteUrl,
                            IsUpdated = link.IsUpdated,
                            UpdateTime = link.UpdateTime
                        };

                        Session.SaveOrUpdate(linkDB);
                        Session.Flush();
                    }
                    else
                    {
                        // 如果能够查找到对应的链接。则可以不用继续查找下去了
                        break;
                    }
                }
            }
            
            return nvOfDB;
        }

        /// <summary>
        /// 更新未下载下来的小说链接
        /// 传入一个Novel，查找到此Novel中所有未更新的链接。之后遍历查找到的结果，下载后更新
        /// </summary>
        /// <param name="novel"></param>
        public void CheckUnDownLoadedLinks(Novel novel)
        {
            List<NovelLink> links = CriticalQuery.GetNeedUpdateLinks(Session, novel);

            foreach (var link in links)
            {
                string srcUrl = link.SiteUrl + link.Href;
                string dstPath = Path.Combine(CommonDef.BaseDir, novel.Name, link.Href);

                if (CommonDef.DownLoadLink(srcUrl, dstPath))
                {
                    link.IsUpdated = true;
                    Session.SaveOrUpdate(link);

                    Session.Flush();
                }
            }
        }


        /// <summary>
        /// 提出所有的Novel。之后依次进行更新
        /// 这里面用到了并行库
        /// </summary>
        /// <returns></returns>
        public bool UpdateAllNovels()
        {
            List<Novel> novels = CriticalQuery.GetAllNovels(Session);
            Parallel.ForEach<Novel>(novels, nv =>
                {
                    UpdateNovel(nv.Name, nv.NovelUrl);
                });

            return true;
        }

        /// <summary>
        /// 根据传递进来的小说名和小说目录链接来更新数据库以及下载未完成的小说
        /// </summary>
        /// <param name="novelName">小说名称（将来可以通过直接在目录中查找小说名）</param>
        /// <param name="strUrl">小说链接地址</param>
        /// <returns>是否成功</returns>
        public bool UpdateNovel(string novelName, string strUrl, bool isForceUpdate = false)
        {
            NovelInfo novelInfo = DownLoadNovel(strUrl);

            Novel novel = CommonDef.GetParser(strUrl).ExtractNovelCatelogLinks(strUrl, novelInfo.Content);

            novel.Name = novelName;
            novel.SiteName = CommonDef.GetSiteName(strUrl);
            novel.NovelUrl = strUrl;

            Novel novelDB = CheckNovel(novel, isForceUpdate);

            CheckUnDownLoadedLinks(novelDB);

            return true;
        }

        /// <summary>
        /// 更新数据库Schema
        /// 只有在数据库结构发生改变的时候才需要使用
        /// 而且一旦数据库定型，也不能够再通过这个进行
        /// </summary>
        public void ExportSchema()
        {
            SchemaManipulate.SchemaExport();
        }

        /// <summary>
        /// 返回小说下载的结构体
        /// 里面包括下载的内容，下载的状态码
        /// </summary>
        /// <param name="strUrl">下载链接</param>
        /// <returns>NovelInfo</returns>
        private NovelInfo DownLoadNovel(string strUrl)
        {
            string strContent;

            WebRequest request = WebRequest.Create(strUrl);
            request.Credentials = CredentialCache.DefaultCredentials;

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            string strStatus = response.StatusDescription;
            HttpStatusCode hStatusCode = response.StatusCode;
            using (Stream dataStream = response.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(dataStream, Encoding.GetEncoding("GB2312")))
                {
                    strContent = reader.ReadToEnd();
                }
            }

            response.Close();

            NovelInfo info = new NovelInfo(strContent, strStatus, hStatusCode);
            return info;
        }
    }
}
